using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.Runtime.InteropServices;
using System.Xml;
using System.Diagnostics;
using System.Security.Principal;

namespace CKS.EBE
{
	public class MetaWeblogHandler
	{
		#region "Win32"
		[DllImport("advapi32.dll", SetLastError = true)]
		static extern bool LogonUser(
		  string principal,
		  string authority,
		  string password,
		  LogonSessionType logonType,
		  LogonProvider logonProvider,
		  out IntPtr token);
		[DllImport("kernel32.dll", SetLastError = true)]
		static extern bool CloseHandle(IntPtr handle);
		enum LogonSessionType : uint
		{
			Interactive = 2,
			Network,
			Batch,
			Service,
			NetworkCleartext = 8,
			NewCredentials
		}
		enum LogonProvider : uint
		{
			Default = 0, // default for platform (use this!)
			WinNT35,     // sends smoke signals to authority
			WinNT40,     // uses NTLM
			WinNT50      // negotiates Kerb or NTLM
		}
		#endregion

		public static void HandleMetaWeblogRequest(HttpApplication context)
		{
			if (LogonMetablogApiUser())
			{
				var m = new API.MetaWeblog();
				var sr = new StreamReader(m.Invoke(context.Request.InputStream));
				context.Response.Write(sr.ReadToEnd());
			}
			context.Response.End();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns>true if logon was sucessfull, false if not</returns>
		protected static bool LogonMetablogApiUser()
		{
			long pos = HttpContext.Current.Request.InputStream.Position;
			var sr = new StreamReader(HttpContext.Current.Request.InputStream);
			string sRequestText = sr.ReadToEnd();
			HttpContext.Current.Request.InputStream.Position = pos;

			var oDoc = new XmlDocument();
			oDoc.LoadXml(sRequestText);

			string sMethodName = oDoc.SelectSingleNode("methodCall/methodName").InnerText;

			XmlNodeList oParameters = oDoc.SelectNodes(".//param");

			bool logonSuccessfull = false;
			if (sMethodName.Contains("getUsersBlogs") || sMethodName.Contains("editPost") || sMethodName.Contains("getCategories") || sMethodName.Contains("getPost") || sMethodName.Contains("getRecentPosts") || sMethodName.Contains("getUserInfo") || sMethodName.Contains("newMediaObject") || sMethodName.Contains("newPost"))
			{
				string sUserName = oParameters[1].SelectSingleNode("value/string").InnerText;
				string sPassword = oParameters[2].SelectSingleNode("value/string").InnerText;
				logonSuccessfull = ValidateAndImpersonateUser(sUserName, sPassword);
			}
			else if (sMethodName.Contains("deletePost"))
			{
				string sUserName = oParameters[2].SelectSingleNode("value/string").InnerText;
				string sPassword = oParameters[3].SelectSingleNode("value/string").InnerText;
				logonSuccessfull = ValidateAndImpersonateUser(sUserName, sPassword);
			}

			if (!logonSuccessfull)
			{
				HttpContext.Current.Response.StatusCode = 401;
				return false;
			}
			return true;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="username"></param>
		/// <param name="password"></param>
		/// <returns>true if logon was sucessfull, false if not</returns>
		protected static bool ValidateAndImpersonateUser(string username, string password)
		{
			IntPtr token;

			string sUser = username;
			string sDomain = "";

			if (username.Contains("/"))
			{
				string[] arUser = username.Split('/');
				sUser = arUser[1];
				sDomain = arUser[0];
			}

			if (username.Contains("\\"))
			{
				string[] arUser = username.Split('\\');
				sUser = arUser[1];
				sDomain = arUser[0];
			}

			bool result = LogonUser(sUser, sDomain,
											password,
											LogonSessionType.Network,
											LogonProvider.Default,
											out token);

			if (result == false)
			{
				Trace.WriteLine("Invalid logon: " + username);
				return false;
			}

			try
			{
				var oIdentity = new WindowsIdentity(token);
				oIdentity.Impersonate();
				// Yes you do need to do this twice...it doesn't work otherwise
				WindowsIdentity.GetCurrent().Impersonate();

				HttpContext.Current.User = new WindowsPrincipal(oIdentity);
			}
			catch (Exception ex)
			{
				Trace.Write(ex.ToString());
				return false;
			}

			return true;
		}
	}
}